/**
 * @file serial.c
 * @brief Serial Interface
 *
 * @version Fixed
 * @author yemit
 * @date 2010-06-17
 */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <key.h>
#include "config.h"
#include "device.h"
#include "bt_cmd.h"
#include "pm.h"

#define SERIAL_BT 6

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

typedef struct {
        int ser_fd;
        char * devs;
}serial_t;

static serial_t sers[] = CONFIG_SERIAL_DEVS;
static int dev_nr = ARRAY_SIZE(sers);
extern int key_fd;

void serial_dev_init(void)
{
        int i;
        for (i = 0; i < dev_nr; i ++) {
                sers[i].ser_fd = open(sers[i].devs, O_RDWR|O_NONBLOCK);
        }
}

#ifdef CONFIG_USB_CDC
int usb_onoff(uint32_t onoff)
{
    if(onoff)
	    return ioctl(sers[4].ser_fd, IOCTL_USB_CONNECT, NULL);
    else
        return ioctl(sers[4].ser_fd, IOCTL_USB_DISCONNECT, NULL);

    return 0;
}
#endif

int serial_init(uint32_t id, serial_info_t * info)
{
#ifdef CONFIG_SERIAL_SWITCH
	int index;
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		index = id;
		id =  CONFIG_SERIAL_MUX_ID;
		if (ioctl(sers[id].ser_fd, IOCTL_SERIAL_SWITCH, (void*)index) < 0)
			return -1;
	}
    
#   ifdef CONFIG_USB_CDC
		if (id == SERIAL_VIR_MAX) {
			id = 4;
		}
#   endif

#endif

	if (id >= dev_nr) {
		return -1;
	}
 
#ifdef CONFIG_BT
	if (id == SERIAL_BT) {
		return 0;
	}
#endif

	if (info) {
		if (ioctl(sers[id].ser_fd,IOCTL_SERIAL_SET_PARAM, info) < 0)
		return -1;
	}
    return 0;
}


int serial_write(uint32_t id, uint8_t * buffer, size_t len)
{
#ifdef CONFIG_SERIAL_SWITCH
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		id =  CONFIG_SERIAL_MUX_ID;
	}

#	ifdef CONFIG_USB_CDC
	if (id == SERIAL_VIR_MAX) {
		id = 4;
	}
#	endif
#endif
	if (id >= dev_nr) {
        return -1;
    }

#ifdef CONFIG_BT
	if (id == SERIAL_BT) {    
		if (len < 128 && len > 3)  {
			char ibuf[128];
			char invalid = 0;
			memcpy(ibuf, buffer, len);
			if(ibuf[len - 1] == '\0')
				invalid = 1;
			if(ibuf[0] == 'A' && ibuf[1] == 'T' \
					&& ibuf[len - 2 - invalid] == '\r' \
					&& ibuf[len - 1 - invalid] == '\n') {
				ibuf[len - 2 - invalid] = '\0';
				if(handle_bt_at(ibuf + 2) > 0)
					return len;
			}
		}
        return bt_write(buffer, len);
	}
#endif

    if (sers[id].ser_fd < 0) {
        return -1;
    }
        if (buffer && len) {
                fd_set writefds;
                struct timeval tvp;
                int maxfd, sendcount, ret = 0 ;

                maxfd = key_fd > sers[id].ser_fd ? key_fd : sers[id].ser_fd;
                maxfd ++;
                sendcount = 0 ;
                tvp.tv_sec = 10 ;
                tvp.tv_usec = 0 ;

                while (sendcount < len) {
                        FD_ZERO(&writefds) ;
                        FD_SET(key_fd, &writefds) ;
                        FD_SET(sers[id].ser_fd, &writefds);
                        ret = select(maxfd, NULL, &writefds, NULL, &tvp) ;
                        if (ret <= 0)
                            break;
                        if (FD_ISSET(sers[id].ser_fd, &writefds)) {
                                ret = write(sers[id].ser_fd, buffer + sendcount, len - sendcount) ;
                                if (ret <= 0)
                                    return ret ;
                                sendcount += ret ;
                        }
                        else {
                                if (read(key_fd, &ret, 1) != 1) continue;
                                if (ret == KEY_ESC) return -2;
                        }
                }
                return sendcount ;
        }
        return 0;
}

int serial_read(uint32_t id, uint8_t * buffer, size_t len, int timeout)
{
    int ret = 0;
    
#ifdef CONFIG_SERIAL_SWITCH
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		id =  CONFIG_SERIAL_MUX_ID;
	}
#	ifdef CONFIG_USB_CDC
	if (id == SERIAL_VIR_MAX) {
		id = 4;
	}
#	endif
#endif
	if (id >= dev_nr) {
         return -1;
    }
    
#ifdef CONFIG_BT
  	if(id == SERIAL_BT){
        ret = bt_cmd_return(buffer, len);
		if (ret == 0)
            ret = bt_read(buffer, len, timeout);
        return ret;
	}
#endif

    if (sers[id].ser_fd < 0) {
        return -1;
    }
        if (buffer && len) {
                fd_set readfds;
                struct timeval tvp, *vp;
                int maxfd, readcount;

                maxfd = key_fd > sers[id].ser_fd ? key_fd : sers[id].ser_fd;
                maxfd ++;
                readcount = 0 ;
                if (timeout == 0) {
                        vp = NULL;
                }
                else {
                        tvp.tv_sec = timeout / 1000;
                        tvp.tv_usec = (timeout % 1000) * 1000 ;
                        vp = &tvp;
                }
                while (readcount < len) {
                        FD_ZERO(&readfds) ;
                        FD_SET(key_fd, &readfds) ;
                        FD_SET(sers[id].ser_fd, &readfds) ;
                        ret = select(maxfd, &readfds, NULL, NULL, vp) ;
                        if (ret <= 0) {
                                break;
                        }
                        if (FD_ISSET(sers[id].ser_fd, &readfds)) {
                                ret = read(sers[id].ser_fd, buffer + readcount, len - readcount);
                                if (ret <= 0) {
                                        break ;
                                }
                                readcount += ret ;
                        }
                        if (FD_ISSET(key_fd, &readfds)) {
                                if (read(key_fd, &ret, 1) != 1) continue;
                                if (ret == KEY_ESC) return -2;
                        }
                }
                return readcount;
        }
        return 0;
}

int serial_clrbuf(uint32_t id, int rx, int tx)
{
#ifdef CONFIG_SERIAL_SWITCH
	if(id >= SERIAL_VIR_MIN && id < SERIAL_VIR_MAX) {
		id =  CONFIG_SERIAL_MUX_ID;
	}
#	ifdef CONFIG_USB_CDC
		if (id == SERIAL_VIR_MAX) {
			id = 4;
		}
#	endif
#endif
	if (id >= dev_nr) {
		return -1;
	}

    if (id == SERIAL_BT) {
#ifdef CONFIG_BT      
        if (rx)
            bt_clrbuf();
		id = CONFIG_BT_UART;
#else
        return -1;
#endif        
	}
    
    if (sers[id].ser_fd < 0) {
        return -1;
    }    
        if (rx)
                ioctl(sers[id].ser_fd,IOCTL_SERIAL_CLR_RXBUF, NULL);
        if (tx)
                ioctl(sers[id].ser_fd,IOCTL_SERIAL_CLR_TXBUF, NULL);
        return 0;
}
